﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.ThreadSafeObjects
{
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
    public class CachedListByKey<TKey, TList, T> : GenericCacheAbstractBase<TList>, ICacheBase<TList>
        where T : class
        where TList : List<T>
    {
        public CachedListByKey(TPopulateCacheDelegate<TList> populateCacheDelegate) : base(populateCacheDelegate) { }
        public CachedListByKey(TPopulateCacheDelegateDirect<TList> populateCacheDelegate) : base(populateCacheDelegate) { }
        public CachedListByKey() { }

        [NonSerialized, XmlIgnore]
        protected ThreadSafeLookup<TKey, TList> _item;

        public override bool IsCacheValid() { return (_item != null && !_dirty); }
        public override bool IsCacheValid(object populateCacheParameter) { return (!_dirty && _item != null && _item[(TKey)populateCacheParameter] != null); }

        public virtual TList GetItem(TKey populateCacheParameter, params object[] args)
        {
            return GetItem((object)populateCacheParameter, args);
        }

        public override TList GetItem(object populateCacheParameter, params object[] args)
        {
            TList item = null;

            if (IsCacheValid(populateCacheParameter))
            {
                item = _item[(TKey)populateCacheParameter];
            }

            if (item == null)
            {
                if (_populateCacheDelegate != null)
                {
                    Interlocked.CompareExchange<Lockable>(ref _repopLock, new Lockable(), null);
                    while (_repopLock.Locked) { Thread.Sleep(50); }
                    _repopLock.AquireLock();
                    {
                        try
                        {
                            if (_dirty)
                            {
                                Interlocked.Exchange<ThreadSafeLookup<TKey, TList>>(ref _item, null);
                                _dirty = false;
                            }

                            if (!IsCacheValid((TKey)populateCacheParameter))
                            {
                                TList outval;
                                if (_populateCacheDelegate(populateCacheParameter, out outval, args))
                                {
                                    if (_item == null) Interlocked.Exchange<ThreadSafeLookup<TKey, TList>>(ref _item, new ThreadSafeLookup<TKey, TList>());
                                    _item.AddOrSet((TKey)populateCacheParameter, (TList)new List<T>(outval));
                                    return outval;
                                }
                            }
                        }
                        finally
                        {
                            _repopLock.ReleaseLock();
                        }
                    }
                }
            }
            return item;
        }

        public override TList GetItem()
        {
            return GetItem(_defaultPopulateParam, _storedArgs);
        }
    }
}
